#include "parsesdt.h"
#include "descriptor.h"
#include "getsection.h"

#define SDT_PID 0x0011
#define SDT_TABLE_ID 0x42

ParseSDT::ParseSDT()
{

}
ParseSDT::~ParseSDT()
{
}

ParseSDT &ParseSDT::get_instance()
{
    static ParseSDT instance;
    return instance;
}

void ParseSDT::ParseSDT_SectionHead(TS_SDT *pstTS_SDT, unsigned char *pucSectionBuffer)
{
    int iSDT_Length = 0;

    pstTS_SDT->table_id = pucSectionBuffer[0];
    pstTS_SDT->section_syntax_indicator = pucSectionBuffer[1] >> 7;
    pstTS_SDT->reserved_future_use_1 = (pucSectionBuffer[1] >> 6) & 0x01;
    pstTS_SDT->reserved_1 = (pucSectionBuffer[1] >> 4) & 0x03;
    pstTS_SDT->section_length = ((pucSectionBuffer[1] & 0x0F) << 8) | pucSectionBuffer[2];
    pstTS_SDT->transport_stream_id = (pucSectionBuffer[3] << 8) | pucSectionBuffer[4];
    pstTS_SDT->reserved_2 = pucSectionBuffer[5] >> 6;
    pstTS_SDT->version_number = (pucSectionBuffer[5] >> 1) & 0x1F;
    pstTS_SDT->current_next_indicator = (pucSectionBuffer[5] << 7) >> 7;
    pstTS_SDT->section_number = pucSectionBuffer[6];
    pstTS_SDT->last_section_number = pucSectionBuffer[7];
    pstTS_SDT->original_network_id = (pucSectionBuffer[8] << 8) | pucSectionBuffer[9];
    pstTS_SDT->reserved_future_use_2 = pucSectionBuffer[10];
    iSDT_Length = pstTS_SDT->section_length + 3;
    pstTS_SDT->CRC_32 = (pucSectionBuffer[iSDT_Length - 4] << 24) | (pucSectionBuffer[iSDT_Length - 3] << 16)
            | (pucSectionBuffer[iSDT_Length - 2] << 8) | pucSectionBuffer[iSDT_Length - 1];
}

int ParseSDT::ParseSDT_Section(TS_SDT *pstTS_SDT, unsigned char *pucSectionBuffer)
{
    int iSDT_Length = 0;
    int iServiceCount = 0;
    int iServicePosition = 11;

    memset(pstTS_SDT, 0, sizeof(TS_SDT));
    ParseSDT_SectionHead(pstTS_SDT, pucSectionBuffer);
    iSDT_Length = pstTS_SDT->section_length;
    for (iServicePosition = 11; iServicePosition < iSDT_Length - 4; iServicePosition += 5)
    {
        pstTS_SDT->astSDT_Service[iServiceCount].service_id = (pucSectionBuffer[iServicePosition] << 8)
                | pucSectionBuffer[iServicePosition + 1];
        pstTS_SDT->astSDT_Service[iServiceCount].reserved_future_use = pucSectionBuffer[iServicePosition + 2] >> 2;
        pstTS_SDT->astSDT_Service[iServiceCount].EIT_schedule_flag = (pucSectionBuffer[iServicePosition + 2] & 0x03) >> 1;
        pstTS_SDT->astSDT_Service[iServiceCount].EIT_present_following_flag = pucSectionBuffer[iServicePosition + 2] & 0x01;
        pstTS_SDT->astSDT_Service[iServiceCount].running_status = pucSectionBuffer[iServicePosition + 3] >> 5;
        pstTS_SDT->astSDT_Service[iServiceCount].free_CA_mode = (pucSectionBuffer[iServicePosition + 3] & 0x1F) >> 4;
        pstTS_SDT->astSDT_Service[iServiceCount].descriptors_loop_length = ((pucSectionBuffer[iServicePosition + 3] & 0x0F) << 8)
                | pucSectionBuffer[iServicePosition + 4];
        if (pstTS_SDT->astSDT_Service[iServiceCount].descriptors_loop_length != 0)
        {
            memcpy(pstTS_SDT->astSDT_Service[iServiceCount].descriptor, pucSectionBuffer + 5 + iServicePosition,
                    pstTS_SDT->astSDT_Service[iServiceCount].descriptors_loop_length);
            iServicePosition += pstTS_SDT->astSDT_Service[iServiceCount].descriptors_loop_length;
        }
        iServiceCount++;
    }
    return iServiceCount;
}
void ParseSDT::GetSDT_Info(TS_SDT *pstTS_SDT, int iServiceCount, SDT_INFO *pstSDT_Info, int *iInfoCount)
{
    int iTemp = 0;
    int iLoopTime = 0;
    SERVICE_DESCRIPTOR stServiceDescriptor = {0};

    for (iLoopTime = 0; iLoopTime < iServiceCount; iLoopTime++)
    {
        pstSDT_Info[*iInfoCount].uiServiceId = pstTS_SDT->astSDT_Service[iLoopTime].service_id;
        pstSDT_Info[*iInfoCount].uiFreeCA_Mode = pstTS_SDT->astSDT_Service[iLoopTime].free_CA_mode;
        pstSDT_Info[*iInfoCount].uiEIT_PresentFollowingFlag = pstTS_SDT->astSDT_Service[iLoopTime].EIT_present_following_flag;
        pstSDT_Info[*iInfoCount].uiEIT_ScheduleFlag = pstTS_SDT->astSDT_Service[iLoopTime].EIT_schedule_flag;
        iTemp = Descriptor::get_instance().GetServiceDescriptor(&stServiceDescriptor, pstTS_SDT->astSDT_Service[iLoopTime].descriptor,
                pstTS_SDT->astSDT_Service[iLoopTime].descriptors_loop_length);
        if (0 == iTemp)
        {
            memcpy(pstSDT_Info[*iInfoCount].aucProgramNanme, stServiceDescriptor.service_name, stServiceDescriptor.service_name_length);
        }
        (*iInfoCount)++;
    }
}

void ParseSDT::CleanSDT_Info(SDT_INFO *pstSDT_Info, int *piInfoCount)
{
    *piInfoCount = 0;
    memset(pstSDT_Info, 0, sizeof(TS_SDT_SERVICE) * SDT_EVENT_MAX);
}

int ParseSDT::ParseSDT_Table(FILE *pfTsFile, int iTsPosition, int iTsLength, SDT_INFO *pstSDT_Info)
{
    int iTemp = 0;
    int iServiceCount = 0;
    int iInfoCount = 0;
    TS_SDT stTS_SDT = {0};
    unsigned int uiVersion = INITIAL_VERSION;
    unsigned int auiRecordSectionNumber[SECTION_COUNT_256] = {0};
    unsigned char aucSectionBuffer[SECTION_MAX_LENGTH_4096] = {0};

    if (-1 == fseek(pfTsFile, iTsPosition, SEEK_SET))
    {
        printf("parse table error\n");
        return -1;
    }
    while (!feof(pfTsFile))
    {
        iTemp = GetSection::get_instance().GetOneSection(pfTsFile, iTsLength, aucSectionBuffer, SDT_PID, SDT_TABLE_ID, &uiVersion);
        if (0 == iTemp) // version number change
        {
            uiVersion = INITIAL_VERSION;
            memset(auiRecordSectionNumber, 0, sizeof(char) * SECTION_COUNT_256); // clean the record array
            fseek(pfTsFile, 0 - iTsLength, SEEK_CUR);
            CleanSDT_Info(pstSDT_Info, &iInfoCount);
        }
        if (1 == iTemp) //one section over
        {
            if (0 == GetSection::get_instance().IsSectionGetBefore(aucSectionBuffer, auiRecordSectionNumber)) //judgment the section whether been parsed before
            {
                iServiceCount = ParseSDT_Section(&stTS_SDT, aucSectionBuffer);
                GetSDT_Info(&stTS_SDT, iServiceCount, pstSDT_Info, &iInfoCount);
            }
            if (1 == GetSection::get_instance().IsAllSectionOver(aucSectionBuffer, auiRecordSectionNumber))
            {
                return iInfoCount;
            }
        }
        if (-1 == iTemp) //error
        {
            printf("no SDT table in the transport stream \n ");
            return 0;
        }
    }
    return 0;
}
